package com.caishi.lkx.exam.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.caishi.lkx.exam.mapper.PackageSkuMapper;
import com.caishi.lkx.exam.model.*;
import com.caishi.lkx.exam.service.*;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.IBaseMapper;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.lock.RedissonLock;
import com.zzw.common.utils.KZEngine;
import com.caishi.lkx.exam.ExamResultCode;
import com.caishi.lkx.exam.ienum.type.ColumnType;

import com.caishi.lkx.order.IAssetsModel;
import com.caishi.lkx.order.ienum.status.AssetsStatus;
import com.caishi.lkx.order.ienum.type.AssetsType;
import com.caishi.lkx.order.model.OrderDetailModel;
import com.caishi.lkx.order.model.OrderModel;
import com.caishi.lkx.order.model.UserAssetsDataModel;
import com.caishi.lkx.order.service.IUserAssetsDataService;
import com.caishi.lkx.order.service.impl.UserAssetsDataServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class PackageSkuServiceImpl implements IPackageSkuService {

    @Resource
    private PackageSkuMapper packageSkuMapper;
    @Resource
    private IProductBaseService productBaseService;
    @Resource
    private IColumnService columnService;

    @Resource
    private IUserAssetsDataService userAssetsDataService;

    @Resource
    private ICategoryService categoryService;
    @Resource
    private IPackageService packageService;
    @Resource
    private IExamService examService;
    @Resource
    private IIndustryService industryService;

    @Override
    public IBaseMapper<PackageSkuModel, String> getMapper() {
        return packageSkuMapper;
    }

    @Override
    public Boolean addOrUpdate(List<PackageSkuModel> packageSkuModels, String packageId) {
        if (CollUtil.isEmpty(packageSkuModels)) {
            this.delete(Wrappers.<PackageSkuModel>lambdaUpdate().eq(PackageSkuModel::getPackageId, packageId));
            return true;
        }
        packageSkuModels.forEach(p -> p.setPackageId(packageId));
        List<PackageSkuModel> updateList = packageSkuModels.stream().filter(p -> StrUtil.isNotBlank(p.getId())).collect(Collectors.toList());
        List<PackageSkuModel> addList = packageSkuModels.stream().filter(p -> StrUtil.isBlank(p.getId())).collect(Collectors.toList());
        List<PackageSkuModel> oldList = this.selectList(Wrappers.<PackageSkuModel>lambdaQuery().eq(PackageSkuModel::getPackageId, packageId));
        if (CollUtil.isNotEmpty(oldList)) {
            List<String> oldIds = oldList.stream().map(BaseEntity::getId).collect(Collectors.toList());
            List<String> updateIds = updateList.stream().map(BaseEntity::getId).collect(Collectors.toList());
            oldIds.removeAll(updateIds);
            if (CollUtil.isNotEmpty(oldIds)) {
                this.delete(oldIds);
            }
        }
        updateList.parallelStream().forEach(this::simpleUpdate);
        if (CollUtil.isNotEmpty(addList)) {
            this.insertBatch(addList);
        }
        return true;
    }

    @Override
    public List<ColumnModel> getEffecitiveColums(String skuId) {
        PackageSkuModel sku = this.getById(skuId);
        if (null == sku) {
            throw new BizRuntimeException(ExamResultCode.skuNotExist);
        }
        if (CollUtil.isEmpty(sku.getProductIds())) {
            throw new BizRuntimeException(ExamResultCode.productNotExist);
        }
        List<ProductBaseModel> productBaseModels = productBaseService.selectBatchIds(sku.getProductIds());
        if (CollUtil.isEmpty(productBaseModels)) {
            throw new BizRuntimeException(ExamResultCode.productNotExist);
        }
        List<String> columnIds = new ArrayList<>();
        productBaseModels.forEach(p -> columnIds.addAll(p.getColumnIds()));
        if (CollUtil.isEmpty(columnIds)) {
            throw new BizRuntimeException(ExamResultCode.columnNotExist);
        }
        List<String> collect = columnIds.stream().distinct().collect(Collectors.toList());
        List<ColumnModel> columnModels = columnService.selectBatchIds(collect);
        if (CollUtil.isEmpty(columnModels)) {
            throw new BizRuntimeException(ExamResultCode.columnNotExist);
        }
        return columnModels;
    }

    @Override
    public List<CategoryModel> getCategories(String skuId) {
        PackageSkuModel model = this.getById(skuId);
        var products = productBaseService.selectBatchIds(model.getProductIds());
        var categoryIds = products.stream().map(ProductBaseModel::getCategoryId).distinct().toList();
        return categoryService.selectBatchIds(categoryIds);
    }

    @Override
    public void addSellCount(String skuId) {
        this.update(null, Wrappers.<PackageSkuModel>lambdaUpdate()
                .setSql("sell_count = sell_count + 1")
                .eq(PackageSkuModel::getId, skuId));
    }

    @Override
    public List<String> selectPackageIdsByExamIdAndProductIds(String examId, List<String> productIds) {
        List<PackageSkuModel> packageSkuModels = this.selectList(Wrappers.<PackageSkuModel>lambdaQuery().eq(PackageSkuModel::getExamId, examId).eq(PackageSkuModel::getEnable, true));
        if (CollUtil.isNotEmpty(packageSkuModels) && CollUtil.isNotEmpty(productIds)) {
            packageSkuModels = packageSkuModels.stream().filter(p -> CollUtil.isNotEmpty(p.getProductIds()) && this.listRetain(p.getProductIds(), productIds)).collect(Collectors.toList());
        }
        return packageSkuModels.stream().map(PackageSkuModel::getPackageId).collect(Collectors.toList());
    }

    @Override
    public Boolean deleteByPackageIds(List<String> packageIds) {
        return this.delete(Wrappers.<PackageSkuModel>lambdaUpdate().in(PackageSkuModel::getPackageId, packageIds)) > 0;
    }

    @Override
    public Boolean deleteByPackageId(String packageId) {
        return this.delete(Wrappers.<PackageSkuModel>lambdaUpdate().eq(PackageSkuModel::getPackageId, packageId)) > 0;
    }

    /**
     * @return java.lang.Boolean
     * @Param: []
     * @Author: huangpeng
     * @Date: 2022/3/10 10:24
     * @Description: 判断两个集合是否有交集
     */
    private Boolean listRetain(List<String> l1, List<String> l2) {
        l1.retainAll(l2);
        return CollUtil.isNotEmpty(l1);

    }

    public static void main(String[] args) {
        List<Map<String, String>> l1 = new ArrayList<>();
        l1.add(MapUtil.<String, String>builder().put("key", "1,2").build());
        l1.add(MapUtil.<String, String>builder().put("key", "1,2,3").build());
        l1.add(MapUtil.<String, String>builder().put("key", "1,2,3,4,5").build());
        List<String> list = l1.stream().flatMap(ma -> Arrays.stream(ma.get("key").split(","))).toList();
        System.err.println(list);
    }

    @Override
    public String orderOnlinePayTitle(OrderModel orderModel, String id) {
        return queryName(id);
    }

    @Override
    public LocalDateTime computeEndTime(OrderModel orderModel, String sourceId, LocalDateTime startTime) {
        PackageSkuModel packageSkuModel = getById(sourceId);
        String packageId = packageSkuModel.getPackageId();
        PackageModel packageModel = packageService.getById(packageId);
        return packageModel.getDays() != null ? startTime.plusDays(packageModel.getDays()) : packageModel.getEndDate().atTime(23, 59, 59);
        // return packageModel.getEndDate() != null ? packageModel.getEndDate().atTime(23, 59, 59) : startTime.plusDays(packageModel.getDays());
    }


    @Override
    public long computePrice(OrderModel orderModel, OrderDetailModel detailModel) {
        var model = getById(detailModel.getEntityId());
        return model.getDiscountPrice();
    }

    @Override
    public boolean createOrderDetailCheck(OrderModel orderModel, OrderDetailModel detailModel) {
        var userId = orderModel.getUserId();
        if (userAssetsDataService.userHaveAsset(userId, AssetsType.product, detailModel.getEntityId())) {
            throw new BizRuntimeException("已购买过产品，无需再次购买");
        }
        return true;
    }

    @Override
    public void assetsIssuePostHandler(OrderModel orderModel, UserAssetsDataModel userAssetsDataModel) {
        var model = getById(userAssetsDataModel.getAssetMappingId());
        var products = productBaseService.selectBatchIds(model.getProductIds());
        var categoryIds = products.stream().map(ProductBaseModel::getCategoryId).distinct().toList();
        var columnIds = products.stream().map(ProductBaseModel::getColumnIds).flatMap(List::stream).distinct().toList();
        //var exams = products.stream().map(ProductBaseModel::getExamIds).flatMap(List::stream).distinct().toList();
        //添加科目权限
        for (var id : categoryIds) {
            var copy = new UserAssetsDataModel();
            BeanUtil.copyProperties(userAssetsDataModel, copy);
            copy.setAssetType(AssetsType.category);
            copy.setAssetMappingId(id.toString());
            copy.setId(null);
            userAssetsDataService.issueAssets(copy, orderModel, true);
        }
        //添加栏目权限
        for (var id : columnIds) {
            var copy = new UserAssetsDataModel();
            BeanUtil.copyProperties(userAssetsDataModel, copy);
            copy.setAssetType(AssetsType.column);
            copy.setAssetMappingId(id);
            copy.setId(null);
            userAssetsDataService.issueAssets(copy, orderModel, true);
        }
        addSellCount(userAssetsDataModel.getAssetMappingId());
    }

    @Override
    public void overduePostHandler(OrderModel lastOrder, UserAssetsDataModel userAssetsDataModel) {
        var model = getById(userAssetsDataModel.getAssetMappingId());
        var products = productBaseService.selectBatchIds(model.getProductIds());
        var categoryIds = products.stream().map(ProductBaseModel::getCategoryId).distinct().toList();
        for (var id : categoryIds) {
            var ml = userAssetsDataService.userAsset(userAssetsDataModel.getUserId(),
                    AssetsType.category,
                    id.toString(),
                    AssetsStatus.effective
            );
            if (ml.isEmpty()) continue;
            var m = ml.get(0);
            if (m.getEndTime().isBefore(userAssetsDataModel.getEndTime().plusMinutes(1))) {
                m.setStatus(AssetsStatus.overdue);
                userAssetsDataService.simpleUpdate(m);
                categoryService.overduePostHandler(lastOrder, m);
            }
        }
    }

    @Override
    public IAssetsModel<PackageSkuModel, String> assetsDetail(String id) {
        PackageSkuModel packageSkuModel = getById(id);
        if (null != packageSkuModel) {
            List<CategoryModel> models = categoryService.selectBatchIds(packageSkuModel.getCategoryIds());
            List<String> list = models.stream().map(CategoryModel::getName).toList();
            packageSkuModel.setCategoryNameList(list);
            List<String> productIds = packageSkuModel.getProductIds();
            List<ProductBaseModel> productBaseModels = productBaseService.selectBatchIds(productIds);
            List<String> columnIds = new ArrayList<>();
            productBaseModels.forEach(e -> columnIds.addAll(e.getColumnIds()));
            if (!columnIds.isEmpty()) {
                List<ColumnModel> columnModels = columnService.selectBatchIds(columnIds);
                List<ColumnType> columnTypes = columnModels.parallelStream().map(ColumnModel::getType).distinct().toList();
                packageSkuModel.setColumnType(columnTypes);
            }
            packageSkuModel.setExamName(examService.queryName(packageSkuModel.getExamId()));
            packageSkuModel.setIndustryName(industryService.queryName(packageSkuModel.getIndustryId()));

        }

        return packageSkuModel;

    }

    @Override
    public void assetsRefundCall(OrderModel orderModel, UserAssetsDataModel userAssetsDataModel) {
        try {

            RedissonLock.lock("product:status:" + userAssetsDataModel.getId(), k -> {
                userAssetsDataService.update(null, Wrappers.<UserAssetsDataModel>lambdaUpdate().eq(UserAssetsDataModel::getId, userAssetsDataModel.getId()).set(UserAssetsDataModel::getStatus, AssetsStatus.invalid));
                String orderId = userAssetsDataModel.getLastOrderId();
                //查找同一个订单的商品
                List<UserAssetsDataModel> userAssetsDataModels = userAssetsDataService.selectList(Wrappers.<UserAssetsDataModel>lambdaQuery()
                        .ne(UserAssetsDataModel::getId, userAssetsDataModel.getId())
                        .last("AND JSON_CONTAINS(order_ids,json_array('" + orderId + "'))")
                );
                userAssetsDataModels.stream().forEach(u -> {
                    //产品不止一个订单
                    if (u.getOrderIds().size() > 1) {
                        //获取产品的所有的订单id
                        List<String> orderIds = u.getOrderIds();
                        orderIds.remove(orderId);
                        //产品的套餐id
                        List<UserAssetsDataModel> models = userAssetsDataService.selectList(Wrappers.<UserAssetsDataModel>lambdaQuery().in(UserAssetsDataModel::getLastOrderId, orderIds).eq(UserAssetsDataModel::getAssetType, AssetsType.product));
                        List<String> packageSkuIdS = models.parallelStream().map(UserAssetsDataModel::getAssetMappingId).toList();
                        LocalDateTime temp = LocalDateTime.now().minusDays(1);
                        for (String packageSkuId : packageSkuIdS) {
                            PackageSkuModel packageSkuModel = this.getById(packageSkuId);
                            if (null != packageSkuModel) {
                                PackageModel packageModel = packageService.getById(packageSkuModel.getPackageId());
                                LocalDateTime endTime = packageModel.getEndDate() != null ? packageModel.getEndDate().atTime(23, 59, 59) : packageModel.getCreatedTime().plusDays(packageModel.getDays());
                                if (temp.isAfter(endTime)) {
                                    temp = endTime;
                                }
                            }
                        }
                        u.setEndTime(temp);
                        if (u.getEndTime().isBefore(LocalDateTime.now())) {
                            u.setStatus(AssetsStatus.invalid);
                        } else {
                            owner(UserAssetsDataServiceImpl.class).userAssetsAutoOverdue(u.getId(), KZEngine.generateKz(LocalDateTime.now(), u.getEndTime().minusSeconds(-5)));
                        }
                        userAssetsDataService.update(u);

                    } else {
                        userAssetsDataService.update(null, Wrappers.<UserAssetsDataModel>lambdaUpdate()
                                .eq(UserAssetsDataModel::getId, u.getId())
                                .set(UserAssetsDataModel::getStatus, AssetsStatus.invalid));
                    }
                });

            });
        } catch (Exception e) {
            throw new BizRuntimeException(e);
        }
    }
}
